Codeforces 833B 题解

题意简述

给定一个序列,长度。定义一个区间的得分为这段区间内不同的数的个数。请你将这个序列划分成段,使得每段的分数加起来最大。

思路

表示前个分成了段的最大得分和,表示内不同的数个数,那么
(这里不知为啥公式爆了,看图凑合一下),其中枚举前面的一个点(即)。
珂是这个转移大概是的,完全不能承受。

观察数据范围,发现时间复杂度大概是,或者带个,根号之类的。

不能用状态数优化这个,那就考虑用数据结构优化。发现只和有关(这就是为什么我把放到了第一维而不是第二维)。

那么依赖的状态就只有一个序列了。然后我们就是要求这个序列的整体最大值,其中第个点的权值是

我们珂以把这个东西想象成这样:初始每个点都是,然后通过一些修改操作(区间加)变成

这样考虑,设表示:上一次出现的位置。(如果没有上一次,那么)那么,对于一个种类,它的贡献就是把区间。然后点的值就是之间加上值的最大值了。

这个维护区间种类数的方式十分常见!!!请各位记好这个trick!!!

(然后解释一下为什么是这样的,如果你能想明白,跳过)
假设序列是这样的:

1
1 5 2 1 4 5

易得数组是这样的:

1
1 1 1 2 1 3

然后我们考虑第一个位置。区间加,也就是
然后就是区间加

加完之后我们发现区间变成了这样:

1
5 4 3 2 1 1

其中这个序列的第个位置表示是最后一个位置)中有多少不同的种类。
区间加的意义是:在区间内,每个位置到末尾都能包含
区间加的意义是:在区间内,每个位置到末尾都能包含

区间加的意义是:在区间内,每个位置到末尾都能包含
也许你会说:那第一个位置到最后一个位置不是也能包含么,但是这一段在算上一个和相同的数(即)的时候,已经被加过了。如果再加一次,就重复了。
(也就是说,只加这一段保证了不会重复,而且也不可能会有遗漏)
(顺便说一句,举一个实例解释问题是很有效的)

然后我们发现,我们珂以拿线段树维护这个东西。对于一个,初始值是,然后区间加即可,最后的值就是查询区间的最大值。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 45000

#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
int n,k,a[N];
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
R1(n),R1(k);
F(i,1,n) R1(a[i]);
}

int dp[51][N];
class SegmentTree
{
public:
struct node
{
int l,r;
int s,a;
}tree[N<<2];
#define ls (index<<1)
#define rs (index<<1|1)

#define L tree[index].l
#define R tree[index].r
#define S tree[index].s
#define A tree[index].a

#define lL tree[ls].l
#define lR tree[ls].r
#define lS tree[ls].s
#define lA tree[ls].a

#define rL tree[rs].l
#define rR tree[rs].r
#define rS tree[rs].s
#define rA tree[rs].a

void Update(int index)
{
S=max(lS,rS);
}
void Build(int pos,int l,int r,int index)
{
L=l,R=r,S=0,A=0;
if (l==r)
{
S=dp[pos][l-1];
return;
}
int mid=(l+r)>>1;
Build(pos,l,mid,ls);
Build(pos,mid+1,r,rs);
Update(index);
}
void AddOne(int x,int index)
{
S+=x;A+=x;
}
void PushDown(int index)
{
if (A)
{
AddOne(A,ls);
AddOne(A,rs);
A=0;
}
}
void Add(int l,int r,int x,int index)
{
if (l>R or L>r) return;
if (l<=L and R<=r) return AddOne(x,index);
PushDown(index);
Add(l,r,x,ls);
Add(l,r,x,rs);
Update(index);
}
int Query(int l,int r,int index)
{
if (l>R or L>r) return 0;
if (l<=L and R<=r) return S;
PushDown(index);
return max(Query(l,r,ls),Query(l,r,rs));
}
}T;
int pos[N];
int pre[N];
void Soviet()
{
F(i,1,n)
{
pre[i]=pos[a[i]]+1;
pos[a[i]]=i;
}
F(i,1,k)
{
T.Build(i-1,1,n,1);
F(j,1,n)
{
T.Add(pre[j],j,1,1);
dp[i][j]=T.Query(1,j,1);
}
}
printf("%d\n",dp[k][n]);
}
void IsMyWife()
{
Input();
Soviet();
}
}
int main()
{
Flandre_Scarlet::IsMyWife();
return 0;
}

w